/*
 * Copyright (C) 2010 TopCoder Inc., All Rights Reserved.
 */
package com.hercules.xre.protocol.testing.handlers.screenshotcapturer;

import com.hercules.xre.protocol.testing.handlers.ScreenshotCapturer;
import com.hercules.xre.protocol.testing.handlers.ScreenshotCapturerException;

import java.awt.AWTException;
import java.awt.Robot;
import java.awt.Toolkit;
import java.awt.datatransfer.Clipboard;
import java.awt.datatransfer.DataFlavor;
import java.awt.datatransfer.Transferable;
import java.awt.datatransfer.UnsupportedFlavorException;
import java.awt.event.KeyEvent;
import java.awt.image.BufferedImage;
import java.io.IOException;

/**
 * This class is the default implementation of {@link ScreenshotCapturer}. It utilizes Java Robot API to perform the
 * screen capture. This class would trigger Alt+PrintScreen key to give command to the OS to get the screenshot of
 * currently active window and store it in the clipboard. This might not be supported in all operating systems.
 * <p>
 * <b>Thread safety:</b> This class is thread safe since it is stateless.
 *
 * @author bramandia, 7realm
 * @version 1.0
 */
public class JavaRobotScreenshotCapturer implements ScreenshotCapturer {
    /** Represents count of allowed failures. */
    private static final int COUNT_OF_ALLOWED_FAILURES = 3;

    /**
     * Empty default constructor.
     */
    public JavaRobotScreenshotCapturer() {
        // empty
    }

    /**
     * Capture the screenshot of the currently active window. If capture failed, this method will retry for 3 times.
     *
     * @return the image of the currently active window
     * @throws ScreenshotCapturerException if there is any error occurred during capturing screenshot
     */
    public BufferedImage captureActiveWindowScreenshot() throws ScreenshotCapturerException {
        int countOfFailures = 0;
        while (true) {
            try {
                // try to get screenshot
                return doCaptureScreenshot();
            } catch (ScreenshotCapturerException e) {
                countOfFailures++;
                if (countOfFailures > COUNT_OF_ALLOWED_FAILURES) {
                    throw e;
                }
                try {
                    Thread.sleep(50);
                } catch (InterruptedException e1) {
                    // ignore this exception, but set count of failure to
                    // maximum in order to report failure after next time
                    countOfFailures = COUNT_OF_ALLOWED_FAILURES;
                }
            }
        }
    }

    /**
     * Do capture of the screenshot of the currently active window.
     *
     * @return the image of the currently active window
     * @throws ScreenshotCapturerException if there is any error occurred during capturing screenshot
     */
    private BufferedImage doCaptureScreenshot() throws ScreenshotCapturerException {
        try {
            // trigger ALT+PrintScreen event to copy screenshot of active window to clipboard
            Robot robot = new Robot();
            robot.keyPress(KeyEvent.VK_ALT);
            robot.keyPress(KeyEvent.VK_PRINTSCREEN);
            robot.keyRelease(KeyEvent.VK_PRINTSCREEN);
            robot.keyRelease(KeyEvent.VK_ALT);

            // transfer image for clipboard to result
            Clipboard clipboard = Toolkit.getDefaultToolkit().getSystemClipboard();
            Transferable contents = clipboard.getContents(null);
            return (BufferedImage) contents.getTransferData(DataFlavor.imageFlavor);
        } catch (AWTException e) {
            throw new ScreenshotCapturerException("Platform configuration does not allow low-level input control.", e);
        } catch (IOException e) {
            throw new ScreenshotCapturerException("Data is not available in clipboard after screenshot was made.", e);
        } catch (SecurityException e) {
            throw new ScreenshotCapturerException("Permission 'createRobot' is not granted.", e);
        } catch (IllegalStateException e) {
            throw new ScreenshotCapturerException("Clipboard is currently unavailable.", e);
        } catch (UnsupportedFlavorException e) {
            throw new ScreenshotCapturerException(
                "Content of clipboard with screenshot doesn't support transfer to Image.", e);
        }
    }
}
